package com.gjp.shiroRealms;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import com.gjp.bean.Resource;
import com.gjp.bean.Roles;
import com.gjp.bean.User;
import com.gjp.service.ResourcesService;
import com.gjp.service.RolesService;
import com.gjp.service.UserService;

/**
 * 对当前用户的登录认证，和授权
 * @author gjp
 *
 */
public class ShiroRealm extends AuthorizingRealm
{
	@Autowired
	UserService userService;
	@Autowired
	RolesService rolesService;
	@Autowired
	ResourcesService resourcesService;
	
	/**
	 * 认证:当调用UsernamePasswordToken的login方法时,执行此方法
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException
	{
		// 根据用户情况，来构建AuthenticationInfo对象并返回,通常使用的是SimpleAuthenticationInfo
		SimpleAuthenticationInfo info = null; 
		
		// 1.把AuthenticationToken转换成UsernamePasswordToken
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;

		// 2.从UsernamePasswordToken中 获取username
		String username = upToken.getUsername();

		// 3.调用数据库方法，从数据库中查询username对应用户记录
		User user = new User();
		user.setUsername(username);
		try {
			user = userService.selectUserByName(user);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 4.若用户不存在则可以抛出AuthenticationException异常
		// 此处有个问题，如果用户输入一个不存在的用户，查出来的数据是null
		// 调用.会报NullPointException异常
		if (user.getUsername() == null && user.getUsername() == "")
		{
			throw new UnknownAccountException("用户不存在");
		}
		
		// 5.根据用户信息的情况，决定是否还需要抛出其他异常

		

		// principal:认证的实体信息，可以是username,也可以是bean对象，邮箱等
		// Object principal = user.getUsername();
		
		// realmName:当前realm对象的name,调用父类的getName()方法获取即可
		// String realmName = getName();
		
		// credentialsSalt: 盐值.
		ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());

		//构建返回对象，将用户名，密码，盐值，当前realm的名称提供给shiro做加密认证
		info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),
				credentialsSalt,  getName());
		return info;
	}

	/**
	 * 授权: 某一个用户在第一次登录时，会调用此方法，查询用户拥有的角色
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
	{
		// 1、从PrincipalCollection中获取登录用户的信息
		Object principal = principals.getPrimaryPrincipal();
		
		// 2、从登录用户的信息，来获取当前用户的角色和权限
		Set<String> roles = new HashSet<String>();
		// 创建用户
		User user = new User();
		//直接将principal强转成字符串
		user.setUsername((String)principal);
		// 查询当前用户详细信息
		user = userService.selectUserByName(user);
		// 将逗号分隔的角色id切分
		String[] rolesIdArr = user.getRolesid().split(",");
		for (int i = 0; i < rolesIdArr.length; i++)
		{
			Roles role = new Roles();
			// 因为数据库中user的rolesid存的是以逗号分隔的数据，所以用的字符串存储的
			// 所以此处应转换成int
			role.setId(Integer.valueOf(rolesIdArr[i]));
			// 查询对应的角色信息
			try {
				role = rolesService.selectRolesById(role);
			} catch (Exception e) {
				e.printStackTrace();
			}
			// 将角色信息存入集合
			roles.add(role.getRolename());
		}
		// 3、创建 SimpleAuthorizationInfo,并设置其属性roles属性.
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.setRoles(roles);
		
		List<Resource> resources = resourcesService.selectResourcesById(user.getRolesid());
		for(Resource r : resources){
			info.addStringPermission(r.getCode());
		}
		
		
		
		
		// 4、返回对象
		return info;
	}

	/**
	 * MD5加密测试方法
	 * @param args
	 */
	public static void main(String[] args)
	{
		String algorithmName = "MD5"; // 加密次数
		Object source = "123456"; // 原密码
		Object salt = ByteSource.Util.bytes("admin"); // 盐
		System.out.println(salt);
		int iterations = 1024; // 加密次数
		SimpleHash simpleHash = new SimpleHash(algorithmName, source, salt,
				iterations);
		System.out.println(simpleHash); // 加密结果
//		String a = "1,2";
//		String[] split = a.split(",");
//		for (int i = 0; i < split.length; i++)
//		{
//			System.out.println(split[i]);
//		}
	}

}
